package edu.ustb.eureka.aop;

import java.lang.reflect.Method;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

import com.netflix.appinfo.InstanceInfo;
import edu.ustb.eureka.contract.RegistrationContract;
import edu.ustb.eureka.contract.RegistrationContractTerm2;
import edu.ustb.sc.definitions.ContractActionInfo;
import edu.ustb.sc.definitions.ContractPartyExecutingMachine;
import edu.ustb.sc.definitions.SpescConditionCheckStatus;
import edu.ustb.sc.definitions.SpescContractTerm;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import org.springframework.stereotype.Component;

@Aspect
@Component
public class LogAspect {

	private final static Logger logger = LoggerFactory.getLogger(LogAspect.class);

	RegistrationContract registrationContract = new RegistrationContract();

	/**
	 * 定义拦截规则：拦截org.springframework.cloud.netflix.eureka.server.InstanceRegistry.register方法
	 */
	@Pointcut("execution(* org.springframework.cloud.netflix.eureka.server.InstanceRegistry.register(..))")
	public void controllerRequestPointcut() {}

	@Around("controllerRequestPointcut()")
	public Object Interceptor(ProceedingJoinPoint pjp) {

		MethodSignature signature = (MethodSignature) pjp.getSignature();
		Method method = signature.getMethod();	// 获取被拦截的方法对象

		Object[] args = pjp.getArgs();
		final InstanceInfo instanceInfo = (InstanceInfo) args[0];
		String partyName = instanceInfo.getAppName();
		Object result = null;
		try {
			ContractPartyExecutingMachine executingEngine = null;
			List<SpescContractTerm> activeTermObjs = null;
			long termCheckingStart = System.currentTimeMillis();
			long termCheckingTime = 0;
			if (partyName.equals("WEATHER-FORECAST")) {
				if ((args.length == 2 && !(Boolean) args[1]) || (args.length == 3 && !(Boolean) args[2])) {    // replication == false
					logger.warn("注册条款检查开始。");
					ContractActionInfo commitAction = new ContractActionInfo(
							RegistrationContract.class.getDeclaredMethod("commit", String.class),
							new Object[] {"WeatherForecast"});
					executingEngine = new ContractPartyExecutingMachine(partyName);
					if (!executingEngine.handleAction(registrationContract, commitAction)) {
						result = "commit执行失败";
					} else {	// commit执行成功，开始当前register动作
						ContractActionInfo registerAction = new ContractActionInfo(method, new Object[] {instanceInfo});
						// 获得和动作相关的条款项
						List<String> relatedTermNames = registrationContract.getActionRelatedTermNames(method);
						// 激活前置条件通过的条款检查对象
						activeTermObjs = relatedTermNames.stream()
								.map(termName -> registrationContract.activateTermObjPassingPreCondition("EUREKA", termName, registerAction))
								.filter(Objects::nonNull)
								.collect(Collectors.toList());
					}

					if (activeTermObjs == null) {
						result = "前置条件检查未通过";
					} else {
						termCheckingTime = System.currentTimeMillis() - termCheckingStart;
					}
				}
			}
			boolean actionDid = false;
			if (result == null) {
				result = pjp.proceed();
				actionDid = true;
			}
			if (actionDid && partyName.equals("WEATHER-FORECAST") &&
					(args.length == 2 && !(Boolean) args[1]) || (args.length == 3 && !(Boolean) args[2])) {
				// 后置条件检查
				long postCheckingStart = System.currentTimeMillis();
				for (SpescContractTerm termObj : activeTermObjs) {
					if (termObj.wherePostCondition(registrationContract, 204) == SpescConditionCheckStatus.No) {
						result = "后置条件检查不通过";
						break;
					}
				}
				logger.warn("注册条款检查结束，耗时：{} ms", termCheckingTime + (System.currentTimeMillis()-postCheckingStart));
			}
		} catch (Throwable e) {
			logger.error("exception: ", e);
			result = "发生异常：" + e.getMessage();
		}

		return result;
	}
}
